#####################################################################
# Product:      KnowledgeEngine (Plone Edition)
#
# File:         SendMail.py
#
# Copyright:    Copyright (c) 2004, Faulkner Technologies
#
# Author:       Brian Oliver
#
# Description:  This file defines the SendMail class, a simple
#               action to send an email
#
# See Also:     Action, Event
#####################################################################

#####################################################################
## Knowledge Engine Library Imports
from Products.KnowledgeEngine.Common import *
from Products.KnowledgeEngine.Core import *
from Products.KnowledgeEngine.Action import Action, ActionInfo
from Products.KnowledgeEngine.PropertyDefinition import *
from Products.KnowledgeEngine.PropertyTypes import *
from Products.KnowledgeEngine.PropertyFormats import *
from Products.KnowledgeEngine.PropertyStructures import *
from Products.KnowledgeEngine.KERFProcessor import *

#####################################################################
## Zope Library Imports
from Globals import DTMLFile
from App.ImageFile import ImageFile

#####################################################################
## Python Library Imports
# (none)

#####################################################################
## SendMailInfo Class Definition
class SendMailInfo(ActionInfo):
    """
        Defines the class info methods, defining information about
        the class without needing an instance of the class
    """
    
    #---------------------------------------------------------------#
    
    def getClassName(self):
        """
            Returns the name of the class for which 
            we are providing information.
        """
        return "SendMail"

    #---------------------------------------------------------------#

    def getDisplayName(self):
        """
            Return a friendly name for the class instances
            (this is used for displaying in lists etc)
        """
    
        return "Send Email"
        
    #---------------------------------------------------------------#

    def getIdentityType(self):
        """
            Return the identity type.  
        """
        
        return "email"
        
    #---------------------------------------------------------------#
    
    def getClassIcon(self):
        """
            Return the appropriate icon file for the class.
        """
        
        return ImageFile('skins/SendMail_icon.gif', globals())
   
    #---------------------------------------------------------------#

    def onConfigureProperties(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register 
            class specific properties using the PropertyReflection interface.
            
            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered
            
            NOTE: This method is called before ClassInfo.onAfterStartup()            
        """

        #initialise the super-class properties for reflection
        ActionInfo.onConfigureProperties(self)

        #remove the 'subject' property due to namespace conflict with Plone 'subject' (used for Keywords
        if self.isPropertyDefined("subject"):
            self.removePropertyDefinition("subject")

        #add the 'emailSubject' property to replace 'subject'       
        self.addPropertyDefinition(PropertyDefinition(identity="emailSubject", displayName="Subject", description="The subject of the message (KERF Friendly)", defaultValue="", propertyType=pt_STRING, propertyFormat=pf_TEXT_SINGLELINE, propertyStructure=ps_ATOMIC))        

        #add the 'to' property        
        self.addPropertyDefinition(PropertyDefinition(identity="recipients", displayName="Recipents", description="A list of reciptient(s) to receive the email.  The recipients may be comma, semi-colon or new-line separated (KERF Friendly). Prefix entries with 'User:' or 'Group:' to email plone users and groups.", defaultValue="", propertyType=pt_STRING, propertyFormat=pf_TEXT_MULTILINE, propertyStructure=ps_ATOMIC))        

        #add the 'from' property        
        self.addPropertyDefinition(PropertyDefinition(identity="sender", displayName="Sender", description="The email account of the sender (eg: mr.sender@domain.com).  This email account must exist in the mail host sending the message", defaultValue="", propertyType=pt_STRING, propertyFormat=pf_TEXT_SINGLELINE, propertyStructure=ps_ATOMIC))        

        #add the 'message' property        
        self.addPropertyDefinition(PropertyDefinition(identity="message", displayName="Message", description="The message (KERF Friendly)", defaultValue="", propertyType=pt_STRING, propertyFormat=pf_TEXT_MULTILINE, propertyStructure=ps_ATOMIC))        

        #add the 'sendSeparateEmails' property        
        self.addPropertyDefinition(PropertyDefinition(identity="sendSeparateEmails", displayName="Send Separate Emails?", description="When there are multiple recipients, should the message be sent separately to each recipient or as one email to all recipients?", defaultValue=false, propertyType=pt_BOOLEAN, propertyFormat=pf_SELECTION + pf_YES_NO, propertyStructure=ps_ATOMIC))
            
    #---------------------------------------------------------------#

#####################################################################
## SendMail Action Class Definition
class SendMail(Action):
    """
        A class to represent a simple folder that may contain Elements
    """

    #---------------------------------------------------------------#

    #ABSTRACT TRANSIENT: a ClassInfo implementation instance to provide info about this class
    _CLASSINFO = SendMailInfo()
    
    #---------------------------------------------------------------#

    def __init__(self, id, identity):
        "Constructor for the Folder"        

        Action.__init__(self, id, identity)
                
        #perform onAfterInstanciation processing
        self.onAfterInstanciation()

    #---------------------------------------------------------------#

    def onAfterInstanciation(self):
        """
            Handle Knowledge Engine specific actions after this object 
            has been loaded into memory.  
            
            This is useful for performing backwards compatibility
        """        
        
        #ensure that the sendSeparateEmails property exists
        if not hasattr(self, "sendSeparateEmails"):
            self.sendSeparateEmails = false

        #migrate 'subject' property to 'emailSubject' property (because 'subject' conflicts with Plone namespace)
        if hasattr(self, 'subject'):
            self.emailSubject = self.subject
            del self.subject
            
        #perform default processing
        Action.onAfterInstanciation(self)

    #---------------------------------------------------------------#

    def onAfterIdentityChange(self, identityMappings):
        """
            Handle Knowledge Engine specific actions after the 
            identity of this object has changed.
            
            The identityMappings dictionary holds 
            mappings from oldIdentities -> newIdentities
            (including this object)
                        
        """

        #perform default processing
        Action.onAfterIdentityChange(self, identityMappings)
        
        #update the KERF friendly properties
        self.emailSubject = updateKERF(self.emailSubject, identityMappings)
        self.recipients = updateKERF(self.recipients, identityMappings)
        self.message = updateKERF(self.message, identityMappings)
        
    #---------------------------------------------------------------#
    
    def getEmailSubject(self):
        """
            Returns the email subject
        """
        
        return self.emailSubject
        
    #---------------------------------------------------------------#
    
    def getRecipients(self):
        """
            Returns the recipients of the email
        """
        
        return self.recipients
        
    #---------------------------------------------------------------#
    
    def getSender(self):
        """
            Returns the sender address of the email
        """
        
        return self.sender
        
    #---------------------------------------------------------------#
    
    def getMessage(self):
        """
            Returns the email message
        """
        
        return self.message
        
    #---------------------------------------------------------------#

    def getCatalogs(self):
        """ return list of catalogs the object should be indexed against
            Note: we are overriding this method so that this class is not catalogged by portal_catalog
        """

        return [ getToolByName(self, 'knowledgeengine_genericobjectcatalogservice', None) ]

    #---------------------------------------------------------------#
    
    def resolveMemberEmails(self, recipients):
        """ Takes a list of recipients, and converts any User or Group references 
            to real email addresses (Preserving existing entries)
        """
        
        # TODO: Could be cleaned up.
        
        emails = []
        
        getUser = self.knowledgeengine_userproviderservice.getUser
        
        for recipient in recipients:
            if recipient.startswith('User:'):
                    
                # This entry is referencing a plone user. Resolve them and get the email address, if any
                user = getUser( recipient[5:] )
                if not user: continue
                
                email = user.getEmail()
                if email:
                    emails.append( email )
                
            elif recipient.startswith('Group:'):
                
                # This entry is referencing a plone group. Get all the members and add their email addresses, if any.
                userIdentities = self.knowledgeengine_groupproviderservice.getUserIdentitiesInGroup( recipient[6:] )
                if not userIdentities: continue
                
                for identity in userIdentities:
                    user = getUser(identity)
                    if not user: continue
                    email = user.getEmail()
                    if email:
                        emails.append( email )
                        
            else:
                emails.append( recipient )
                
        return emails                
            
                
                
    #---------------------------------------------------------------#
    
    def getResolvedRecipients(self, assessment):
        """ Gets the recipients property, parses it, and resolves plone member info """
        
        #resolve properties containing kerfs
        recipients = resolveKERF(self.getRecipients(), assessment)           
        
        #create a list of recipient email addresses
        #(split the recipient string into a list of lines eg: [string])
        recipients = recipients.splitlines(false)

        #(split each line using commas to produce [[string]] and then folder back to [string])
        recipients = reduce(lambda x,y:x+y, [line.split(",") for line in recipients])           

        #(split each line using semi-colons to produce [[string]] and then folder back to [string])
        recipients = reduce(lambda x,y:x+y, [line.split(";") for line in recipients])           

        #(strip unnecessary spaces)
        recipients = [recipient.strip() for recipient in recipients]

        # Resolve any User or Group references in the recipient list
        recipients = self.resolveMemberEmails(recipients)        
        
        
        return recipients
    
    #---------------------------------------------------------------#
    
    
    def execute(self, assessment, user = None, previousActionSuccess = true):
        """
           Sends the message for this log action to the stdout
        """            

        #get the MailHost so we can send an email
        mailhost = self.MailHost
                         
        recipients = self.getResolvedRecipients( assessment )
        
        subject = resolveKERF(self.getEmailSubject(), assessment)
        message = resolveKERF(self.getMessage(), assessment)

        #determine if we are to send separate emails?
        sendSeparateEmails = self.getPropertyValue("sendSeparateEmails")
        

        #---------------------------------------------------------------------
        #determine if we should send as one email or multiple?
        if sendSeparateEmails:
            #send an email for each recipient
            for recipient in recipients:                   
                #try send to an email to the current recipient
                try:
                    mailhost.secureSend(message, recipient, self.getSender(), subject)
                except:
                    #todo: we need to log the failure here!
                    raise
        else:
            #send one email for all recipients
            #(create a string of all recipients, comma separated)
            recipients = ",".join(recipients)

            #try to send a single email to all recipients
            try:
                mailhost.secureSend(message, recipients, self.getSender(), subject)
            except:
                #todo: we need to log the failure here!
                raise

            
        #return that the action did something        
        return true        
        
    #---------------------------------------------------------------#

#####################################################################
## Class Initialisation

# Register Class Information with the Knowledge Engine
registerGenericObjectClass(SendMail)

#####################################################################